package com.ycy.ss.tumblr;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import net.oauth.OAuthAccessor;
import net.oauth.OAuthMessage;
import net.oauth.OAuthProblemException;
import net.oauth.ParameterStyle;
import net.oauth.client.OAuthResponseMessage;
import net.oauth.example.consumer.webapp.CookieConsumer;
import net.oauth.http.HttpMessage;
import net.oauth.http.HttpResponseMessage;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.commons.httpclient.HttpStatus;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.ycy.ss.SSUtil;
import com.ycy.ss.sync.AllType;
import com.ycy.ss.tumblr.po.TumblrBlogInfo;
import com.ycy.ss.tumblr.po.TumblrPostAnswer;
import com.ycy.ss.tumblr.po.TumblrPostAudio;
import com.ycy.ss.tumblr.po.TumblrPostBase;
import com.ycy.ss.tumblr.po.TumblrPostChat;
import com.ycy.ss.tumblr.po.TumblrPostChatDialogue;
import com.ycy.ss.tumblr.po.TumblrPostLink;
import com.ycy.ss.tumblr.po.TumblrPostPhotos;
import com.ycy.ss.tumblr.po.TumblrPostPhotosPhoto;
import com.ycy.ss.tumblr.po.TumblrPostPhotosPhotoAltSize;
import com.ycy.ss.tumblr.po.TumblrPostPhotosPhotoOriginalSize;
import com.ycy.ss.tumblr.po.TumblrPostQuote;
import com.ycy.ss.tumblr.po.TumblrPostText;
import com.ycy.ss.tumblr.po.TumblrPostVideo;
import com.ycy.ss.tumblr.po.TumblrPostVideoPlayer;
import com.ycy.ss.tumblr.po.TumblrUserInfo;
import com.ycy.ss.tumblr.po.TumblrUserInfoBlog;


public class TumblrUtil{
	private static Logger logger = LogManager.getLogger(TumblrUtil.class);

	// /info
	public static TumblrBlogInfo queryBlogInfo(OAuthAccessor accessor, String baseHostname){
		String apiUrl = "http://api.tumblr.com/v2/blog/" + baseHostname + "/info";
		JSONObject res = getJsonResult(accessor, apiUrl + "?api_key=" + accessor.consumer.consumerKey);

		TumblrBlogInfo blogInfo = null;
		if (res != null) {
			blogInfo = (TumblrBlogInfo) JSONObject.toBean(res.getJSONObject("response").getJSONObject("blog"),
					TumblrBlogInfo.class);
		}
		return blogInfo;
	}

	// /user/info
	public static TumblrUserInfo queryUserInfo(OAuthAccessor accessor){
		String apiUrl = "http://api.tumblr.com/v2/user/info";
		JSONObject res = getJsonResult(accessor, apiUrl);

		TumblrUserInfo userInfo = null;
		if (res != null) {
			userInfo = (TumblrUserInfo) JSONObject.toBean(res.getJSONObject("response").getJSONObject("user"),
					TumblrUserInfo.class);
			//
			List<TumblrUserInfoBlog> blogs = new ArrayList<TumblrUserInfoBlog>();
			JSONArray arr = res.getJSONObject("response").getJSONObject("user").getJSONArray("blogs");
			for (int i = 0; i < arr.size(); i++) {
				TumblrUserInfoBlog blog = (TumblrUserInfoBlog) JSONObject.toBean(arr.getJSONObject(i),
						TumblrUserInfoBlog.class);
				blogs.add(blog);
			}
			userInfo.setBlogs(blogs);
		}
		return userInfo;
	}

	// /user/dashboard
	// param:
	// limit Number The number of results to return: 1–20, inclusive 20 No
	// offset Number Post number to start at 0 (first post) No
	// type String The type of post to return. Specify one of the following: text, photo, quote, link, chat, audio,
	// video, question None – return all types No
	// since_id Number Return posts that have appeared after this ID
	// Use this parameter to page through the results: first get a set of posts, and then get posts since the last ID of
	// the previous set. 0 No
	// reblog_info Boolean Indicates whether to return reblog information (specify true or false). Returns the various
	// reblogged_ fields. False No
	// notes_info Boolean Indicates whether to return notes information (specify true or false). Returns note count and
	// note metadata. False No
	public static List<TumblrPostBase> queryUserDashboardPosts(OAuthAccessor accessor, Map<String, String> paramMap){
		String apiUrl = "http://api.tumblr.com/v2/user/dashboard";
		JSONObject res = getJsonResult(accessor, apiUrl + genParams(paramMap, true));

		List<TumblrPostBase> posts = new ArrayList<TumblrPostBase>();
		if (res != null) {
			JSONArray _posts = res.getJSONObject("response").getJSONArray("posts");
			for (int i = 0; i < _posts.size(); i++) {
				JSONObject _post = _posts.getJSONObject(i);
				TumblrPostBase post = (TumblrPostBase) JSONObject.toBean(_post, typeToClass(_post.getString("type")));
				dealPostInfo(_post, post);
				posts.add(post);
			}
		}
		return posts;
	}

	// /posts
	// param:
	// base-hostname String The standard or custom blog hostname,See the Overview for more details. N/A Yes
	// api_key String Your OAuth Consumer Key See Authentication for more details. N/A Yes
	// type String The type of post to return. Specify one of the following: text, quote, link, answer, video, audio,
	// photo None – return all types No
	// id Number A specific post ID. Returns the single post specified or (if not found) a 404 error. None No
	// tag String Limits the response to posts with the specified tag None No
	// limit Number The number of posts to return: 1–20, inclusive 20 No
	// offset Number Post number to start at 0 (first post) No
	// reblog_info Boolean Indicates whether to return reblog information (specify true or false). Returns the various
	// reblogged_ fields. False No
	// notes_info Boolean Indicates whether to return notes information (specify true or false). Returns note count and
	// note metadata. False No
	// format String Specifies the post format to return, other than HTML:
	// text – Plain text, no HTML
	// raw – As entered by the user (no post-processing); if the user writes in Markdown, the Markdown will be returned
	// rather than HTML
	// None (HTML) No
	public static List<TumblrPostBase> queryPosts(OAuthAccessor accessor, String baseHostname,
			Map<String, String> paramMap){
		String apiUrl = "http://api.tumblr.com/v2/blog/" + baseHostname + "/posts";
		JSONObject res = getJsonResult(accessor, apiUrl + "?api_key=" + accessor.consumer.consumerKey
				+ genParams(paramMap, false));

		List<TumblrPostBase> posts = new ArrayList<TumblrPostBase>();
		if (res != null) {
			JSONArray _posts = res.getJSONObject("response").getJSONArray("posts");
			for (int i = 0; i < _posts.size(); i++) {
				JSONObject _post = _posts.getJSONObject(i);
				TumblrPostBase post = (TumblrPostBase) JSONObject.toBean(_post, typeToClass(_post.getString("type")));
				dealPostInfo(_post, post);
				posts.add(post);
			}
		}
		return posts;
	}

	public static String newPost(OAuthAccessor accessor, String baseHostname, TumblrPostBase post){
		String apiUrl = "http://api.tumblr.com/v2/blog/" + baseHostname + "/post?api_key="
				+ accessor.consumer.consumerKey;

		String date = SSUtil.getCurrentGmtTime(0, "yyyy-MM-dd HH:mm:ss") + " GMT";

		Map<String, String> params = new HashMap<String, String>();
		params.put("state", "published");
		params.put("date", date);

		try {
			String type = post.getType();
			if (AllType.TEXT.equals(type)) {
				TumblrPostText text = (TumblrPostText) post;
				String body = text.getBody();
				params.put("type", "text");
				params.put("body", body);

			} else if (AllType.PHOTO.equals(type)) {
				TumblrPostPhotos photos = (TumblrPostPhotos) post;
				String caption = photos.getCaption();
				String source = photos.getSource_url();
				params.put("type", "photo");
				params.put("caption", caption);
				params.put("source", source);

			} else if (AllType.LINK.equals(type)) {
				TumblrPostLink link = (TumblrPostLink) post;
				String title = link.getTitle();
				String url = link.getUrl();
				String description = link.getDescription();
				params.put("type", "link");
				params.put("title", title);
				params.put("description", description);
				params.put("url", url);
			} else {
				logger.warn("does not support this type:" + type);
				return null;
			}

			OAuthResponseMessage response = CookieConsumer.CLIENT.access(accessor.newRequestMessage(OAuthMessage.POST,
					apiUrl, params.entrySet()), ParameterStyle.AUTHORIZATION_HEADER);
			int status = response.getHttpResponse().getStatusCode();
			logger.info("newPost ok,response code is:" + status);
			if (status != HttpStatus.SC_OK) {
				logger.warn("newPost status is not 200,current is:" + status);
				BufferedReader br = new BufferedReader(new InputStreamReader(response.getHttpResponse().getBody()));
				String a = null;
				logger.warn("body start..");
				while ((a = br.readLine()) != null) {
					logger.warn(a);
				}
				logger.warn("body end..");
				br.close();
				return null;
			}
		} catch (Exception e) {
			logger.warn("", e);
			date = null;
		}

		return date;
	}

	private static void dealPostInfo(JSONObject _post, TumblrPostBase post){
		String type = _post.getString("type");
		if ("photo".equals(type)) {
			List<TumblrPostPhotosPhoto> photos = new ArrayList<TumblrPostPhotosPhoto>();
			JSONArray arr = _post.getJSONArray("photos");
			for (int i = 0; i < arr.size(); i++) {
				TumblrPostPhotosPhoto photo = (TumblrPostPhotosPhoto) JSONObject.toBean(arr.getJSONObject(i),
						TumblrPostPhotosPhoto.class);
				List<TumblrPostPhotosPhotoAltSize> altSizes = new ArrayList<TumblrPostPhotosPhotoAltSize>();

				JSONArray arr2 = arr.getJSONObject(i).getJSONArray("alt_sizes");
				for (int j = 0; j < arr2.size(); j++) {
					TumblrPostPhotosPhotoAltSize altSize = (TumblrPostPhotosPhotoAltSize) JSONObject.toBean(arr2
							.getJSONObject(i), TumblrPostPhotosPhotoAltSize.class);
					altSizes.add(altSize);
				}
				photo.setAlt_sizes(altSizes);

				TumblrPostPhotosPhotoOriginalSize originalSize = (TumblrPostPhotosPhotoOriginalSize) JSONObject.toBean(
						arr.getJSONObject(i).getJSONObject("original_size"), TumblrPostPhotosPhotoOriginalSize.class);
				photo.setOriginal_size(originalSize);
				photos.add(photo);
			}
			((TumblrPostPhotos) post).setPhotos(photos);
		} else if ("chat".equals(type)) {
			List<TumblrPostChatDialogue> dialogues = new ArrayList<TumblrPostChatDialogue>();
			JSONArray arr = _post.getJSONArray("dialogue");
			for (int i = 0; i < arr.size(); i++) {
				TumblrPostChatDialogue dialogue = (TumblrPostChatDialogue) JSONObject.toBean(arr.getJSONObject(i),
						TumblrPostChatDialogue.class);

				dialogues.add(dialogue);
			}
			((TumblrPostChat) post).setDialogue(dialogues);
		} else if ("video".equals(type)) {
			List<TumblrPostVideoPlayer> player = new ArrayList<TumblrPostVideoPlayer>();
			JSONArray arr = _post.getJSONArray("player");
			for (int i = 0; i < arr.size(); i++) {
				TumblrPostVideoPlayer p = (TumblrPostVideoPlayer) JSONObject.toBean(arr.getJSONObject(i),
						TumblrPostVideoPlayer.class);

				player.add(p);
			}
			((TumblrPostVideo) post).setPlayer(player);
		}
	}

	public static String typeToAllType(String type){
		if ("text".equals(type)) {
			return AllType.TEXT;
		} else if ("link".equals(type)) {
			return AllType.LINK;
		} else if ("video".equals(type)) {
			return AllType.VIDEO;
		} else if ("audio".equals(type)) {
			return AllType.AUDIO;
		} else if ("photo".equals(type)) {
			return AllType.PHOTO;
		}
		return null;
	}

	private static Class typeToClass(String type){
		if ("text".equals(type)) {
			return TumblrPostText.class;
		} else if ("quote".equals(type)) {
			return TumblrPostQuote.class;
		} else if ("chat".equals(type)) {
			return TumblrPostChat.class;
		} else if ("link".equals(type)) {
			return TumblrPostLink.class;
		} else if ("answer".equals(type)) {
			return TumblrPostAnswer.class;
		} else if ("video".equals(type)) {
			return TumblrPostVideo.class;
		} else if ("audio".equals(type)) {
			return TumblrPostAudio.class;
		} else if ("photo".equals(type)) {
			return TumblrPostPhotos.class;
		}
		return null;
	}

	private static String genParams(Map<String, String> paramMap, boolean isFirstParam){
		StringBuilder sb = new StringBuilder(16);
		if (paramMap != null && paramMap.size() != 0) {
			for (Entry<String, String> e : paramMap.entrySet()) {
				String k = e.getKey();
				String v = e.getValue();
				if (k != null && k.length() != 0) {
					sb.append(k + "=" + ((v != null && v.length() != 0) ? v : "") + "&");
				}
			}
		}
		return sb.length() == 0 ? "" : (isFirstParam ? "?" : "&") + sb.toString();
	}

	private static JSONObject getJsonResult(OAuthAccessor accessor, String apiUrl){
		JSONObject res = null;
		OAuthResponseMessage response = null;
		BufferedReader br = null;
		try {
			response = CookieConsumer.CLIENT.access(accessor.newRequestMessage(OAuthMessage.GET, apiUrl, null),
					ParameterStyle.AUTHORIZATION_HEADER);
			int status = response.getHttpResponse().getStatusCode();
			if (status != HttpResponseMessage.STATUS_OK) {
				OAuthProblemException problem = response.toOAuthProblemException();
				if (problem.getProblem() != null) {
					throw problem;
				}
				Map<String, Object> dump = problem.getParameters();
				logger.warn("getJsonResult,status != HttpResponseMessage.STATUS_OK");
				logger.warn(dump.get(HttpMessage.REQUEST));
				logger.warn("----------------------");
				logger.warn(dump.get(HttpMessage.RESPONSE));
			} else {
				br = new BufferedReader(new InputStreamReader(response.getBodyAsStream()));
				StringBuilder sb = new StringBuilder(128);
				String str = null;
				while ((str = br.readLine()) != null) {
					sb.append(str);
				}
				br.close();
				logger.debug(sb.toString());
				res = JSONObject.fromObject(sb.toString());
			}
		} catch (Exception e) {
			logger.warn("", e);
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					logger.warn("", e);
				}
			}
		}
		return res;
	}

	// public static void main(String[] args){
	// JSONObject str = JSONObject
	// .fromObject("{\"blog_name\":\"staff\",\"photos\":[{\"caption\":\"\",\"alt_sizes\":[{\"width\":500,\"height\":237,\"url\":\"http://abc.png\"},]}]}");
	// TumblrPostBase post = (TumblrPostBase) JSONObject.toBean(str, TumblrPostPhotos.class);
	// System.out.println(post);
	// }

}
